package org.metasketch.editor.toolbox;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import javafx.scene.Node;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;

import org.metasketch.demo.model.DEMOModel;
import org.metasketch.diagram.javafx.graphics.BaseGraphic;
import org.metasketch.editor.timeline.SnapshotManager;
import org.metasketch.editor.toolbox.advanced.ISemanticTypeTester;
import org.metasketch.graphics.GraphicContainer;
import org.metasketch.graphics.GraphicNode;
import org.metasketch.graphics.Point;
import org.metasketch.graphics.registry.IGraphic;
import org.metasketch.javafx.graphics.JFXDiagram;
import org.metasketch.model.BaseModel;
import org.metasketch.model.CommonIdentified;

public class DefaultToolHandler implements IToolHandler, ISemanticTypeTester {

	private String semanticBuildScript;
	ISemanticFactory semanticFactory;
	Class semanticType;
	Class graphicType;
	String graphicTypeName;
	boolean createOverEmptySpace;
	private RelationManager relationManager;
	String semanticTestScript;

	public DefaultToolHandler(Class semanticType, Class graphicType,
			String graphicTypeName, boolean createOverEmptySpace,
			RelationManager relationManager) {
		this.semanticType = semanticType;
		this.graphicType = graphicType;
		this.graphicTypeName = graphicTypeName;
		this.createOverEmptySpace = createOverEmptySpace;
		this.relationManager = relationManager;
	}

	public DefaultToolHandler(ISemanticFactory factory, Class graphicType,
			String graphicTypeName, boolean createOverEmptySpace,
			RelationManager relationManager) {
		this.semanticFactory = factory;
		this.semanticType = semanticType;
		this.graphicType = graphicType;
		this.graphicTypeName = graphicTypeName;
		this.createOverEmptySpace = createOverEmptySpace;
		this.relationManager = relationManager;
	}

	public DefaultToolHandler(String script, Class graphicType,
			String graphicTypeName, boolean createOverEmptySpace,
			RelationManager relationManager) {
		this.semanticBuildScript = script;
		this.semanticType = semanticType;
		this.graphicType = graphicType;
		this.graphicTypeName = graphicTypeName;
		this.createOverEmptySpace = createOverEmptySpace;
		this.relationManager = relationManager;
	}

	public DefaultToolHandler(Class semanticType, String script,
			Class graphicType, String graphicTypeName,
			boolean createOverEmptySpace, RelationManager relationManager) {
		this.semanticType = semanticType;
		this.semanticBuildScript = script;
		this.semanticType = semanticType;
		this.graphicType = graphicType;
		this.graphicTypeName = graphicTypeName;
		this.createOverEmptySpace = createOverEmptySpace;
		this.relationManager = relationManager;
	}

	@Override
	public void handle(MouseEvent event, JFXDiagram jfxDiagram, BaseModel model) {
		try {
			if (event.getEventType() == MouseEvent.MOUSE_CLICKED
					&& event.getClickCount() == 1
					&& event.getButton() == MouseButton.PRIMARY) {

				Object source = event.getSource();
				treatCreationEvent(
						new Point((int) event.getX(), (int) event.getY()),
						event, jfxDiagram, model, source, null, true);

			}
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public IGraphic treatCreationEvent(MouseEvent event, JFXDiagram jfxDiagram,
			BaseModel model, Object dropTarget, CommonIdentified semantic,
			boolean b) throws Throwable {
		return treatCreationEvent(
				new Point((int) event.getX(), (int) event.getY()), event,
				jfxDiagram, model, dropTarget, semantic, b);

	}

	public IGraphic treatCreationEvent(Point coords, MouseEvent event,
			JFXDiagram jfxDiagram, BaseModel model, Object source,
			CommonIdentified semanticObject, boolean takeSnapshot)
			throws InstantiationException, IllegalAccessException, Throwable {
		if (!(source instanceof BaseGraphic)) {

			if (!createOverEmptySpace) {
				// not sure this should work
				event.consume();
				return null;
			}

			// clicked over empty space

			CommonIdentified semantic = semanticObject;

			if (semantic == null) {
				semantic = createNewInstance();
				model.addChild(semantic);
			}

			IGraphic nodeGraphic = setupMainGraphic(coords, jfxDiagram, model,
					semantic);

			event.consume();

			if (takeSnapshot)
				SnapshotManager.takeSnapshot();

			return nodeGraphic;
		} else {
			// clocked over a graphical node...
			BaseGraphic baseGraphic = (BaseGraphic) source;

			if (!(baseGraphic.getGraphicNode() instanceof GraphicContainer)) {
				event.consume();
				return null;
			}

			GraphicContainer parentContainer = (GraphicContainer) baseGraphic
					.getGraphicNode();

			CommonIdentified parentSemantic = baseGraphic.getSemantic();
			Class parentType = baseGraphic.getSemantic().getClass();

			BaseRelation relation = relationManager.getParentRelation(
					getProperSemanticType(), parentType);
			if (relation == null) {
				event.consume();
				return null;
			}

			CommonIdentified semantic = semanticObject;

			if (semantic == null) {
				semantic = createNewInstance();
				model.addChild(semantic);
			}
			// setup relation fields between the objects...
			relation.setup(semantic, parentSemantic);

			IGraphic nodeGraphic = setupParentChildGraphic(coords, jfxDiagram,
					model, baseGraphic, parentContainer, semantic);

			event.consume();

			if (takeSnapshot)
				SnapshotManager.takeSnapshot();

			return nodeGraphic;

		}
	}

	private IGraphic setupMainGraphic(Point coords, JFXDiagram jfxDiagram,
			BaseModel model, CommonIdentified semantic)
			throws InstantiationException, IllegalAccessException {
		IGraphic node = setupBaseGraphic(coords, jfxDiagram, model, semantic);

		// jfxDiagram.get
		jfxDiagram.getDiagram().getGraphics().add(node.getGraphicNode());
		jfxDiagram.addNode((Node) node);

		return node;
	}

	private IGraphic setupParentChildGraphic(Point coords,
			JFXDiagram jfxDiagram, BaseModel model, BaseGraphic baseGraphic,
			GraphicContainer parentContainer, CommonIdentified semantic)
			throws InstantiationException, IllegalAccessException {
		IGraphic node = setupBaseGraphic(coords, jfxDiagram, model, semantic);

		// jfxDiagram.get
		// jfxDiagram.getDiagram().getGraphics()
		// .add(node.getGraphicNode());
		//
		parentContainer.addNode(node.getGraphicNode());
		baseGraphic.getChildren().add((Node) node);
		jfxDiagram.addNodeListeners((Node) node);

		return node;
	}

	private IGraphic setupBaseGraphic(Point coords, JFXDiagram jfxDiagram,
			BaseModel model, CommonIdentified semantic)
			throws InstantiationException, IllegalAccessException {
		IGraphic node = (IGraphic) graphicType.newInstance();

		GraphicNode graphicNode = (GraphicNode) node.getGraphicNode();

		graphicNode.setSemantic(semantic.getId());
		graphicNode.setType(graphicTypeName);

		graphicNode.setX(coords.getX());
		graphicNode.setY(coords.getY());

		node.setup(semantic, jfxDiagram.getDiagram(), model);
		return node;
	}

	private CommonIdentified createNewInstance() throws InstantiationException,
			IllegalAccessException {

		CommonIdentified semantic = null;

		if (semanticFactory != null) {
			semantic = semanticFactory.getNewInstance();
		} else if (semanticType != null) {
			semantic = (CommonIdentified) semanticType.newInstance();
			// return semantic;
		}

		if (semanticBuildScript != null) {
			Binding binding = new Binding();
			binding.setVariable("semantic", semantic);
			binding.setVariable("type", semanticType);
			GroovyShell shell = new GroovyShell(binding);

			Object result = shell.evaluate(semanticBuildScript);

			if (semantic == null)
				semantic = (CommonIdentified) result;
		}

		return semantic;

	}

	public Class getProperSemanticType() {
		if (semanticFactory != null) {
			return semanticFactory.getSemanticType();
		}

		return semanticType;
	}

	@Override
	public void cleanup() {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean isValid(CommonIdentified semantic) {
		if (semanticTestScript == null)
			return getProperSemanticType() == semantic.getClass();

		if (getProperSemanticType() != semantic.getClass())
			return false;

		Binding binding = new Binding();
		binding.setVariable("semantic", semantic);
		binding.setVariable("type", semantic.getClass());
		GroovyShell shell = new GroovyShell(binding);

		boolean result = (Boolean) shell.evaluate(semanticTestScript);

		return result;

	}

	public String getSemanticTestScript() {
		return semanticTestScript;
	}

	public void setSemanticTestScript(String semanticTestScript) {
		this.semanticTestScript = semanticTestScript;
	}

}
